/**prueba
 * 
 * 
 * @author byoms23
 */
public class Analizador {
    // Declarar variables de instancia
    private Matriz[] matrices;
    
    public Analizador() {
        // Dar valor a las variables de instancia
        matrices = new Matriz[0];
    }
    
    public void analizar(String operacion) 
        throws ExcepcionOperacionNoEncontrada, ExcepcionDeclararMatriz, 
        ExcepcionCantidadFilas, ExcepcionCantidadColumnas, ExcepcionColumnasPorFila,
        ExcepcionMaximoColumnas, ExcepcionMaximoFilas, ExcepcionSumaMatricesNoCompatibles,
        ExcepcionMultiplicacionMatricesNoCompatibles {
        
        // Declarar variables y objetos
        int limite = 0; 
        
        // Est�ndarizar la operaci�n pedida
        operacion = espacios(operacion);
        
        // Realizar la operaci�n pedida.
        if(operacion.length() <= 0) {
            
            // ERROR DE INSTRUCCION
            
            throw (new ExcepcionOperacionNoEncontrada());
            
        } else if(operacion.charAt(0) == '#') {
            
            // CREAR NUEVA MATRIZ
            
            // Declarar variables y objetos 
            Integer[][] direccionar = new Integer[0][0];
            Integer[][] espacios = new Integer[0][1];
     
            // Obtener los datos primordiales de la matriz 
            limite = operacion.indexOf("=");
            
            // Revisar los caracteres ingresados
            for(int i = limite; i < operacion.length(); i++) {

            	// Revisar el caracter actual
                if(operacion.charAt(i) == '[') {
                    // Guardar el inicio de un nuevo segmento
                    direccionar = (Integer[][])(expandir(direccionar, 2));
                    direccionar[direccionar.length - 1][0] = i;
                    
                } else if(operacion.charAt(i) == ']') {
                    // Buscar el �ltimo segmento abierto
                	for(int fila = direccionar.length - 1; fila >= 0; fila--) {

                	    if(direccionar[fila][1] == null) {
                		    // Guardar la posici�n en la posici�n correcta
                			direccionar[fila][1] = i;
                			break;

                	    } else if(fila == 0) {
                		    // Verificar que no haya escrito ']' de m�s
                		    throw (new ExcepcionDeclararMatriz());
                		}
                	}
                	
                } else if (operacion.charAt(i) == ' ') {
                    // Guardar el inicio de la nueva columna
                    espacios = expandir(espacios, 1);
                    espacios[espacios.length - 1][0] = i;
                }
            }
            
            // Verificar que se hayan cerrado todos los segmentos
            if(direccionar[0][1] == null) {
                throw (new ExcepcionDeclararMatriz());
            }
                        
            // Verificar que se haya insertado cuando menos una fila para la matriz 
            if(direccionar.length < 2) {
                throw (new ExcepcionCantidadFilas());
            }
            
            // Verificar que no se haya ingresado mas de 10 filas
            if(direccionar.length > 11) {
            	throw (new ExcepcionMaximoFilas());
            }
            
            // Verificar que haya ingresado cuando menos una columna por cada fila
            if(direccionar[1][0] + 1 == direccionar[1][1]) {
                throw (new ExcepcionCantidadColumnas());
            }
            
            // Verificar que todas las filas tienen el mismo n�mero de columnas
            int columnas = 1, columnasAnterior = 1, filas = 1;
            for(int i = 0; i < espacios.length; i++) {

                // revisar si la columna aun esta en la misma fila 
                if(espacios[i][0] > direccionar[filas][1]) {
                    
                    
                    // Revisar que no de error en la primer fila
                    if((columnasAnterior == 1) && (columnas > columnasAnterior)) {
                        columnasAnterior = columnas;
                    }
                    
                    // Tirar error de columnas
                    if(columnas != columnasAnterior) {
                        throw (new ExcepcionColumnasPorFila());
                    }
                    
                    // Iniciar datos para la siguiente fila
                    columnas = 2;
                    filas++;
                    
                } else {
                	
                    // Dar nuevo valor de columnas
                    columnas++;
                }
                
                /*
                System.out.println("Inicio...: (" + i + "-" + filas + ") " +
                        System.out.println("Fin de fila." );
                System.out.println("Final....: (" + i + "-" + filas + ") " +
                        "Espacio: " + espacios[i][0] + "/" + direccionar[filas][1] + 
                        "\tColumnas: " + columnas +  
                        "\tAnterior: " + columnasAnterior);
                */
            }
            
            // revisar que no de error en la primer fila
            if((columnasAnterior == 1) && (columnas > columnasAnterior)) {
                columnasAnterior = columnas;
            }
            
            // Tirar error de columnas
            if(columnas != columnasAnterior) {
                throw (new ExcepcionColumnasPorFila());
            }

            // Comprobar que las acciones anteriores se realizaron en todas las filas
            if((filas < direccionar.length - 1) && (columnasAnterior != 1)) {
                throw (new ExcepcionColumnasPorFila());
            }
            
            // Verificar que no se haya ingresado mas de 10 columnas
            if(columnas > 10) {
            	throw (new ExcepcionMaximoColumnas());
            }
            	
            // Crear la matriz (de numeros)
            int inicio = 0, fin = 0, field = 0; 
            Integer[][] numeros = new Integer[direccionar.length - 1][columnas];
            for(int i = 0; i < numeros.length; i++) {
            	for(int j = 0; j < numeros[i].length; j++) {
            		
            		// Buscar inicio de numero
            		if(j == 0) {
            			inicio = direccionar[i + 1][0] + 1;
            		} else {
            			inicio = espacios[field - 1][0] + 1;
            		}
            		
            		// Buscar fin del numero
            		if(j >= columnas - 1 || espacios[field][0] >= direccionar[i + 1][1]) {
            			fin = direccionar[i + 1][1];
            		} else {
            			fin = espacios[field][0];
            			field++;
            		}
            		
            		/*
            		System.out.println(i + " - " + j);
            		System.out.println(inicio + " % " + fin);
            		System.out.println(operacion.substring(inicio, fin) + ".");
            		*/
            		
            		// Guardar el numero actual
            		numeros[i][j] = Integer.parseInt(operacion.substring(inicio, fin));
            	}
            }
            
            // Crear nueva matriz
        	agregarMatriz(operacion.substring(1, limite), numeros);
                        
        } else if(operacion.equals("salir")) {
        } else if(operacion.equals("ayuda")) {
            
            // MOSTRAR EL MEN� DE AYUDA
            
            System.out.println("\n\n=====================\n" +
                    "Instrucciones: \n" +
                    "=====================\n" +
                    "Ingresar una nueva matriz: \n" +
                    "1. Ingresar el caracter \'#\'. \n" +
                    "2. Ingresar el nombre de la matriz (sin dejar espacio, seran omitidos).\n" +
                    "3. Ingresar el signo \'=\'. \n" +
                    "4. Ingresar las filas y las columnas de la matriz utilizando " +
                    "la notaci�n de signos \'[\' y \']\'" +
                    "\n\n=====================\n" +
                    "la notaci�n de signos \'[\' y \']\'"
                    );
            
        } else if(operacion.charAt(0) == '+') {
            
            // HACER UNA SUMA DE MATRICES
        	
        	Operador operar = new Operador();
        	int[][] respuesta = (operar.suma(matrices[matrices.length - 1].getDatos(), 
        			matrices[matrices.length - 2].getDatos()));
        	
        	System.out.println();        	
        	System.out.println("RESPUESTA:");
        	for(int i = 0; i < respuesta.length; i++) {
            	for(int j = 0; j < respuesta[i].length; j++) {
            		System.out.print(respuesta[i][j] + " ");
            	}
            	System.out.println();
            }            

        } else if(operacion.charAt(0) == '-') {
            
            // HACER UNA RESTA DE MATRICES
        	
        	Operador operar = new Operador();
        	int[][] respuesta = (operar.resta(matrices[matrices.length - 1].getDatos(), 
        			matrices[matrices.length - 2].getDatos()));
        	
        	System.out.println();        	
        	System.out.println("RESPUESTA:");
        	for(int i = 0; i < respuesta.length; i++) {
            	for(int j = 0; j < respuesta[i].length; j++) {
            		System.out.print(respuesta[i][j] + " ");
            	}
            	System.out.println();
            }            

        	
        } else if(operacion.charAt(0) == '*') {
            
            // HACER UNA MULTIPLICACI�N DE MATRICES

        	Operador operar = new Operador();
    	   	int[][] respuesta = operar.multiplicar(matrices[matrices.length - 1].getDatos(), 
        			matrices[matrices.length - 2].getDatos());
    	   	
        	System.out.println();        	
        	System.out.println("RESPUESTA:");
        	for(int i = 0; i < respuesta.length; i++) {
            	for(int j = 0; j < respuesta[i].length; j++) {
            		System.out.print(respuesta[i][j] + " ");
            	}
            	System.out.println();
            }            
        	
        } else if(operacion.charAt(0) == '&') {
            
            // HACER UNA TRANSVERSION DE MATRICES
        	
        	Operador operar = new Operador();
        	int[][] respuesta = operar.swapper(matrices[matrices.length - 1].getDatos());
        	
        	System.out.println();        	
        	System.out.println("RESPUESTA:");
        	for(int i = 0; i < respuesta.length; i++) {
            	for(int j = 0; j < respuesta[i].length; j++) {
            		System.out.print(respuesta[i][j] + " ");
            	}
            	System.out.println();
            }            

        	
        } else {
            
            // ERROR DE OPERACI�N INV�LIDA
            
            throw (new ExcepcionOperacionNoEncontrada());
        }
    }
    
    private Integer[][] expandir(Integer[][] original, int columnas) {
        // Declarar variables y objetos
        Integer[][] nuevo = new Integer[original.length + 1][columnas];
        
        // Hacer la copia de la matriz anterior
        for(int i = 0; i < original.length; i++) {
            for(int j = 0; j < original[i].length; j++)
                nuevo[i][j] = original[i][j];
        }
        
        // Devolver la nueva matriz
        return nuevo;
    }
    
    /**
     * 
     * 
     * @param nombre
     * @param datos
     */
    private int agregarMatriz(String nombre, Integer[][] datos) {
        // Declarar variables y objetos
        Matriz[] temporal = new Matriz[matrices.length];
        Matriz matriz = new Matriz(nombre, datos);
        int resultado = matrices.length + 1;
        
        // Hacer la copia de la matriZ anterior
        for(int i = 0; i < matrices.length; i++) {
                temporal[i] = matrices[i];
        }
        
        // Agregar espacio al final
        matrices = new Matriz[resultado];
        
        // Regresar datos a la matriz
        for(int i = 0; i < temporal.length; i++) {
            matrices[i] = temporal[i];
        }
        
        // Asignar la nueva matriz
        matrices[matrices.length -1] = matriz;
        
        // Devolver posicion
        return resultado;
    }
    
    /**
     * Elimina los espacios en blanco de una cadena de texto dada. Deja los espacios en blanco
     * de la parte comprendida entre los corchetes, '[' y ']'.  
     * 
     * @param cadena La cadena de texto a la que se le quiere quitar los espacios en blanco.
     * @return Cadena de texto sin los espacios en blanco innecesarios. 
     */
    private String espacios(String cadena) {
        // Declarar varibles y objetos
        boolean dejar = false, espacio = true;
        int posicion = -1;
        String respuesta = "";
        
        // Revisar caracter por caracter
        for(int i = 0; i < cadena.length(); i++) {
            if(cadena.charAt(i) == ' ') {
                if((dejar) && (posicion != i - 1)) {
                    // Agregar caracter
                    respuesta += cadena.charAt(i);
                    espacio = true;
                }
                // Recordar �ltima posici�n y caracter
                posicion = i;
            } else { 
                // Revisar si deja ingresar espacios
                if(cadena.charAt(i) == '[') {
                    dejar = true;
                    posicion = i;
                } else if(cadena.charAt(i) == ']') {
                    dejar = false;
                    posicion = i;
                    // Borrar si hay espacios anteriores al cierre
                    if(espacio) {
                        respuesta = respuesta.substring(0, respuesta.length() -1);
                    }
                }

                // Agregar caracter
                respuesta += cadena.charAt(i);
                espacio = false;
            }
        }
        
        // Devolver resultado
        return respuesta;
    }
}